@@ -1,4 +1,3 @@
|
|||||||
console.log('Running Something')
|
|
||||||
require('dotenv').config({ path: './config/.env' })
|
require('dotenv').config({ path: './config/.env' })
|
||||||
const express = require('express')
|
const express = require('express')
|
||||||
const databaseConnection = require('./setup/databaseConnection.js')
|
const databaseConnection = require('./setup/databaseConnection.js')
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ const belongingSchema = new mongoose.Schema(
|
|||||||
type: ObjectID,
|
type: ObjectID,
|
||||||
ref: 'Category',
|
ref: 'Category',
|
||||||
},
|
},
|
||||||
|
unit: {
|
||||||
|
type: String,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
description: String,
|
description: String,
|
||||||
relatedProjects: [String],
|
relatedProjects: [String],
|
||||||
location: {
|
location: {
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
const catchAsync = require('../../globalErrorHandler/catchAsync')
|
||||||
|
const transactionService = require('./transactionService')
|
||||||
|
|
||||||
|
exports.createTransaction = catchAsync(async (req, res, next) => {
|
||||||
|
const transactions = await transactionService.createTransaction(req.body)
|
||||||
|
|
||||||
|
res.status(201).json({
|
||||||
|
status: 'success',
|
||||||
|
results: transactions.length,
|
||||||
|
data: transactions,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.getUserTransaction = catchAsync(async (req, res, next) => {
|
||||||
|
const transactions = await transactionService.getUserTransaction(req.user._id)
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
status: 'success',
|
||||||
|
results: transactions.length,
|
||||||
|
data: transactions,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.getBelongingInventory = catchAsync(async (req, res, next) => {
|
||||||
|
const result = await transactionService.getBelongingInventory(req.params.belongingId)
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
status: 'success',
|
||||||
|
data: result,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.updateTransaction = catchAsync(async (req, res, next) => {
|
||||||
|
const transaction = await transactionService.updateTransaction(req.params.transactionId, req.body)
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
status: 'success',
|
||||||
|
data: transaction,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
exports.deleteTransaction = catchAsync(async (req, res, next) => {
|
||||||
|
await transactionService.deleteTransaction(req.params.transactionId)
|
||||||
|
|
||||||
|
res.status(204).json({
|
||||||
|
status: 'success',
|
||||||
|
data: null,
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
const mongoose = require('mongoose')
|
||||||
|
const ObjectID = mongoose.Schema.Types.ObjectId
|
||||||
|
|
||||||
|
const transactionSchema = new mongoose.Schema(
|
||||||
|
{
|
||||||
|
type: {
|
||||||
|
type: String,
|
||||||
|
enum: ['withdraw', 'deposit'],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
status: {
|
||||||
|
type: String,
|
||||||
|
enum: ['failed', 'pending', 'finished'],
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
source: {
|
||||||
|
type: String,
|
||||||
|
enum: ['normal'],
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: Number,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
date: {
|
||||||
|
type: Date,
|
||||||
|
default: Date.now(),
|
||||||
|
},
|
||||||
|
description: String,
|
||||||
|
userId: {
|
||||||
|
type: ObjectID,
|
||||||
|
ref: 'User',
|
||||||
|
},
|
||||||
|
belongingId: {
|
||||||
|
type: ObjectID,
|
||||||
|
ref: 'Belonging',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
toJSON: { virtuals: true },
|
||||||
|
toObject: { virtuals: true },
|
||||||
|
timestamps: true,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add individual indexes for frequently queried fields
|
||||||
|
transactionSchema.index({ type: 1 })
|
||||||
|
transactionSchema.index({ status: 1 })
|
||||||
|
transactionSchema.index({ value: 1 })
|
||||||
|
|
||||||
|
// Add compound indexes for common query patterns in aggregation
|
||||||
|
transactionSchema.index({ belongingId: 1, status: 1 })
|
||||||
|
transactionSchema.index({ belongingId: 1, type: 1, status: 1 })
|
||||||
|
|
||||||
|
const Transaction = mongoose.model('Transaction', transactionSchema)
|
||||||
|
module.exports = Transaction
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
const express = require('express')
|
||||||
|
const transactionController = require('./transactionController')
|
||||||
|
// const perm = require('../../middlewares/permissionMiddleware')
|
||||||
|
const loginCheck = require('../../middlewares/loginCheck')
|
||||||
|
|
||||||
|
const router = new express.Router()
|
||||||
|
|
||||||
|
router.use(loginCheck.loginProtect)
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/admin/self',
|
||||||
|
// perm.requires('getallbelongings'),
|
||||||
|
transactionController.getUserTransaction
|
||||||
|
)
|
||||||
|
|
||||||
|
router.post(
|
||||||
|
'/admin/create',
|
||||||
|
// perm.requires('getallbelongings'),
|
||||||
|
transactionController.createTransaction
|
||||||
|
)
|
||||||
|
|
||||||
|
router.get(
|
||||||
|
'/admin/belonging/:belongingId/inventory',
|
||||||
|
// perm.requires('getallbelongings'),
|
||||||
|
transactionController.getBelongingInventory
|
||||||
|
)
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
'/admin/update/:transactionId',
|
||||||
|
// perm.requires('getallbelongings'),
|
||||||
|
transactionController.updateTransaction
|
||||||
|
)
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
'/admin/delete/:transactionId',
|
||||||
|
// perm.requires('getallbelongings'),
|
||||||
|
transactionController.deleteTransaction
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = router
|
||||||
@@ -0,0 +1,150 @@
|
|||||||
|
const Transaction = require('./transactionModel')
|
||||||
|
const Belonging = require('../belonging/belongingModel')
|
||||||
|
const AppError = require('../../globalErrorHandler/AppError')
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
const ObjectID = mongoose.Schema.Types.ObjectId
|
||||||
|
const { client } = require('../../setup/typesense')
|
||||||
|
const TsApiFeatures = require('../../utils/TsApiFeatures')
|
||||||
|
|
||||||
|
const filterObj = (obj, ...allowedFields) => {
|
||||||
|
const newObj = {}
|
||||||
|
Object.keys(obj).forEach((el) => {
|
||||||
|
if (allowedFields.includes(el)) newObj[el] = obj[el]
|
||||||
|
})
|
||||||
|
|
||||||
|
return newObj
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTransaction = async (data) => {
|
||||||
|
if (data.type == 'withdraw') {
|
||||||
|
// Aggregate transactions to calculate inventory
|
||||||
|
const result = await Transaction.aggregate([
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
belongingId: ObjectID,
|
||||||
|
status: 'finished',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: '$belongingId',
|
||||||
|
totalInventory: {
|
||||||
|
$sum: '$value',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
if (result[0].totalInventory <= 0) throw new AppError('Invalid Transaction', 404, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return Transaction.create({
|
||||||
|
type: data.type,
|
||||||
|
status: data.status,
|
||||||
|
source: data.source,
|
||||||
|
value: data.type === 'withdraw' ? data.value * -1 : data.value,
|
||||||
|
date: data.Date,
|
||||||
|
description: data.description,
|
||||||
|
userId: data.userId,
|
||||||
|
belongingId: data.belongingId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const getUserTransaction = async (userId) => {
|
||||||
|
return Transaction.find({ userId }, null, null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const getBelongingInventory = async (belongingId) => {
|
||||||
|
const belonging = await Belonging.findById(belongingId, null, null)
|
||||||
|
|
||||||
|
if (!belonging) {
|
||||||
|
throw new AppError('NoBelongingFound', 404, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Simplified aggregation to calculate inventory metrics
|
||||||
|
const result = await Transaction.aggregate([
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
belongingId: ObjectID,
|
||||||
|
$or: [{ status: 'finished' }, { status: 'pending' }],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$facet: {
|
||||||
|
// Calculate total inventory from finished transactions
|
||||||
|
finishedInventory: [
|
||||||
|
{ $match: { status: 'finished' } },
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: null,
|
||||||
|
total: { $sum: '$value' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Calculate pending deposits
|
||||||
|
pendingDepositsInventory: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
status: 'pending',
|
||||||
|
type: 'deposit',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: null,
|
||||||
|
total: { $sum: '$value' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
// Calculate pending withdrawals
|
||||||
|
pendingWithdrawsInventory: [
|
||||||
|
{
|
||||||
|
$match: {
|
||||||
|
status: 'pending',
|
||||||
|
type: 'withdraw',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$group: {
|
||||||
|
_id: null,
|
||||||
|
total: { $sum: '$value' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// Restructure the output to get only the values
|
||||||
|
$project: {
|
||||||
|
finishedInventory: { $ifNull: [{ $arrayElemAt: ['$finishedInventory.total', 0] }, 0] },
|
||||||
|
pendingDepositsInventory: { $ifNull: [{ $arrayElemAt: ['$pendingDepositsInventory.total', 0] }, 0] },
|
||||||
|
pendingWithdrawsInventory: { $ifNull: [{ $arrayElemAt: ['$pendingWithdrawsInventory.total', 0] }, 0] },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
return { belongingCode: belonging.belongingCode, ...result[0] }
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTransaction = async (transactionId, data) => {
|
||||||
|
const filteredBody = filterObj(data, 'status')
|
||||||
|
const transaction = await Transaction.findOneAndUpdate({ _id: transactionId }, filteredBody, { new: true })
|
||||||
|
|
||||||
|
if (!transaction) {
|
||||||
|
throw new AppError('NoTransactionFound', 404, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return transaction
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTransaction = async (transactionId) => {
|
||||||
|
const transaction = await Transaction.findOneAndDelete({ _id: transactionId })
|
||||||
|
|
||||||
|
if (!transaction) {
|
||||||
|
throw AppError('NoTransactionFound', 404, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
module.exports = { createTransaction, getUserTransaction, getBelongingInventory, updateTransaction, deleteTransaction }
|
||||||
@@ -5,8 +5,8 @@ const loginCheck = require('../../middlewares/loginCheck')
|
|||||||
|
|
||||||
const router = new express.Router()
|
const router = new express.Router()
|
||||||
|
|
||||||
router.post('/user/signup-email', userController.signup)
|
router.post('/signup-email', userController.signup)
|
||||||
router.post('/user/login-email', userController.loginUser)
|
router.post('/login-email', userController.loginUser)
|
||||||
router.post('/logout', userController.logoutUser)
|
router.post('/logout', userController.logoutUser)
|
||||||
|
|
||||||
router.use(loginCheck.loginProtect)
|
router.use(loginCheck.loginProtect)
|
||||||
|
|||||||
+2
-2
@@ -1,8 +1,8 @@
|
|||||||
MONGODB_URL=mongodb://root:DBKvf305VCc0csA5@services.irn9.chabokan.net:20690/marbm?authSource=admin
|
MONGODB_URL=mongodb://root:yBi0FXMb1rWNhFxu@services.irn12.chabokan.net:58178/my-app?authSource=admin
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
PORT=3000
|
PORT=3000
|
||||||
JWT_SECRET=Y@RJ$WT!@K0NS@MPL!
|
JWT_SECRET=Y@RJ$WT!@K0NS@MPL!
|
||||||
REDIS_URL= redis://:JrKdaxaQQGTwpxgm@services.irn9.chabokan.net:6322
|
REDIS_URL= redis://:GFis6zU4DXW9Q7NQ@services.irn12.chabokan.net:19794/0
|
||||||
LOGIN_ATTEMPT_LIMIT=900
|
LOGIN_ATTEMPT_LIMIT=900
|
||||||
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
SUPER_ADMIN_ROLE_ID=66a2da5b9b989a8909948848
|
||||||
TYPE_SENSE_URL=services.irn9.chabokan.net
|
TYPE_SENSE_URL=services.irn9.chabokan.net
|
||||||
|
|||||||
+10
-10
@@ -1,13 +1,13 @@
|
|||||||
services:
|
services:
|
||||||
# api-server:
|
# api-server:
|
||||||
# build: ./
|
# build: ./
|
||||||
# ports:
|
# ports:
|
||||||
# - 3000:3000
|
# - 3000:4000
|
||||||
# environment:
|
# environment:
|
||||||
# - MONGODB_URL=mongodb://admin:*135792468*@mongo:27017/marbm?authSource=admin
|
# - MONGODB_URL=mongodb://admin:*135792468*@bm_mongo:27017/marbm?authSource=admin
|
||||||
# - NODE_ENV=development
|
# - NODE_ENV=development
|
||||||
# - PORT=3000
|
# - PORT=3000
|
||||||
# - REDIS_URL=redis://:135792468@redis:6379/0
|
# - REDIS_URL=redis://:135792468@bm_redis:6379/0
|
||||||
bm_minio:
|
bm_minio:
|
||||||
image: minio/minio
|
image: minio/minio
|
||||||
ports:
|
ports:
|
||||||
@@ -42,7 +42,7 @@ services:
|
|||||||
image: typesense/typesense:27.1
|
image: typesense/typesense:27.1
|
||||||
command: ./typesense-server
|
command: ./typesense-server
|
||||||
ports:
|
ports:
|
||||||
- "8108:8108"
|
- '8108:8108'
|
||||||
environment:
|
environment:
|
||||||
TYPESENSE_API_KEY: vYQq7pTgl6ACoRy
|
TYPESENSE_API_KEY: vYQq7pTgl6ACoRy
|
||||||
TYPESENSE_DATA_DIR: /data
|
TYPESENSE_DATA_DIR: /data
|
||||||
|
|||||||
Generated
+1815
-1815
File diff suppressed because it is too large
Load Diff
+32
-32
@@ -1,36 +1,36 @@
|
|||||||
{
|
{
|
||||||
"name": "belonging-manager",
|
"name": "belonging-manager",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
"main": "app.js",
|
"main": "app.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"seed": "node ./seeder/seed.js",
|
"seed": "node ./seeder/seed.js",
|
||||||
"start": "node app.js",
|
"start": "nodemon app.js",
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "ISC",
|
||||||
"description": "",
|
"description": "",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"axios": "^1.7.9",
|
"axios": "^1.7.9",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"body-parser": "^1.20.3",
|
"body-parser": "^1.20.3",
|
||||||
"chalk": "^4.1.2",
|
"chalk": "^4.1.2",
|
||||||
"compression": "^1.7.5",
|
"compression": "^1.7.5",
|
||||||
"cookie-parser": "^1.4.7",
|
"cookie-parser": "^1.4.7",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"dotenv": "^16.4.7",
|
"dotenv": "^16.4.7",
|
||||||
"express": "^4.21.2",
|
"express": "^4.21.2",
|
||||||
"jsonwebtoken": "^9.0.2",
|
"jsonwebtoken": "^9.0.2",
|
||||||
"minio": "^8.0.3",
|
"minio": "^8.0.3",
|
||||||
"mongoose": "^8.9.0",
|
"mongoose": "^8.9.0",
|
||||||
"mongoose-sequence": "^6.0.1",
|
"mongoose-sequence": "^6.0.1",
|
||||||
"multer": "^1.4.5-lts.1",
|
"multer": "^1.4.5-lts.1",
|
||||||
"redis": "^4.7.0",
|
"redis": "^4.7.0",
|
||||||
"typesense": "^1.8.2",
|
"typesense": "^1.8.2",
|
||||||
"validator": "^13.12.0"
|
"validator": "^13.12.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^3.1.9"
|
"nodemon": "^3.1.9"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ const roleRoutes = require('../components/role/roleRoutes')
|
|||||||
const permissionRoutes = require('../components/permission/permissionRoutes')
|
const permissionRoutes = require('../components/permission/permissionRoutes')
|
||||||
const belongingRoutes = require('../components/belonging/belongingRoutes')
|
const belongingRoutes = require('../components/belonging/belongingRoutes')
|
||||||
const categoryRoutes = require('../components/category/categoryRoutes')
|
const categoryRoutes = require('../components/category/categoryRoutes')
|
||||||
|
const transactionRoutes = require('../components/transaction/transactionRoutes')
|
||||||
|
// const codeRoutes = require('../components/code/codeRoutes')
|
||||||
|
// const CRMRoutes = require('../components/pages/crmRoutes')
|
||||||
|
// const LoginRoute = require('../components/pages/loginRoutes')
|
||||||
|
|
||||||
const fileRoutes = require('../components/file/fileRoutes')
|
const fileRoutes = require('../components/file/fileRoutes')
|
||||||
const featureRoutes = require('../components/feature/featureRoutes')
|
const featureRoutes = require('../components/feature/featureRoutes')
|
||||||
const routerEndpoints = new express.Router()
|
const routerEndpoints = new express.Router()
|
||||||
@@ -13,6 +18,8 @@ routerEndpoints.use('/permissions', permissionRoutes)
|
|||||||
routerEndpoints.use('/belongings', belongingRoutes)
|
routerEndpoints.use('/belongings', belongingRoutes)
|
||||||
routerEndpoints.use('/roles', roleRoutes)
|
routerEndpoints.use('/roles', roleRoutes)
|
||||||
routerEndpoints.use('/categories', categoryRoutes)
|
routerEndpoints.use('/categories', categoryRoutes)
|
||||||
|
routerEndpoints.use('/transactions', transactionRoutes)
|
||||||
|
|
||||||
routerEndpoints.use('/files', fileRoutes)
|
routerEndpoints.use('/files', fileRoutes)
|
||||||
routerEndpoints.use('/features', featureRoutes)
|
routerEndpoints.use('/features', featureRoutes)
|
||||||
module.exports = routerEndpoints
|
module.exports = routerEndpoints
|
||||||
|
|||||||
Reference in New Issue
Block a user