Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
03b279d
feat: Added Kitchen Inventory Management Processing Projects API endp…
abhinav-TB Jan 30, 2026
c05028a
feat(processing): add batches and unit fields to processing project m…
abhinav-TB Feb 22, 2026
ca78899
feat: implement calendar event management API with support for kitche…
abhinav-TB Mar 2, 2026
c1456ba
feat: add transplanting and harvesting event models, controllers, and…
abhinav-TB Mar 11, 2026
8075a77
feat: Implement API endpoints and Mongoose models for planting, trimm…
abhinav-TB Mar 18, 2026
da6b1d5
fix(harvesting): resolve SonarQube NoSQL injection security hotspot i…
abhinav-TB May 6, 2026
66751c1
fix(harvesting): resolve SonarQube NoSQL injection security hotspot i…
abhinav-TB May 6, 2026
d9a98f3
fix(calendar): resolve SonarQube NoSQL injection and merge development
abhinav-TB May 6, 2026
948b448
fix: change promotion-eligibility endpoint from GET to POST to resolv…
sayali-2308 May 7, 2026
99db09d
fix(weeklyReport): Fixed Stars Visible on Hours Logged
DiyaWadhwani May 8, 2026
53e605b
Merge pull request #2205 from OneCommunityGlobal/Diya_Fix_TimeLoggedS…
one-community May 8, 2026
357a9eb
Merge pull request #2201 from OneCommunityGlobal/Sayali_Fix_Promotion…
one-community May 8, 2026
aad51d8
Merge pull request #2097 from OneCommunityGlobal/Abhinav-Kitchen-Inve…
one-community May 8, 2026
ecdfdcf
Merge pull request #2109 from OneCommunityGlobal/Abhinav-Kitchen-Inve…
one-community May 8, 2026
56d1b86
Merge branch 'development' into Abhinav-Kitchen-Inventory-Management-…
abhinav-TB May 8, 2026
50a2e67
Merge branch 'development' into Abhinav-Kitchen-Inventory-Management-…
abhinav-TB May 8, 2026
87a6772
Merge pull request #2082 from OneCommunityGlobal/Abhinav-Kitchen-Inve…
one-community May 8, 2026
7e659ba
Merge branch 'development' into Abhinav-Kitchen-Inventory-Management-…
abhinav-TB May 8, 2026
8a200e7
Merge pull request #2025 from OneCommunityGlobal/Abhinav-Kitchen-Inve…
one-community May 8, 2026
84ecaee
fix(sonarqube): Fixed Sonarqube Duplication Issues
DiyaWadhwani May 8, 2026
e772669
fix(lint): fixed husky lint issues
DiyaWadhwani May 8, 2026
bef6dba
Merge pull request #2207 from OneCommunityGlobal/Diya_Fix_BackendToMa…
one-community May 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const express = require('express');
const Sentry = require('@sentry/node');
const testRoutes = require('./routes/testRoutes');

Check warning on line 3 in src/app.js

View workflow job for this annotation

GitHub Actions / Lint Check

There should be no empty line between import groups

const app = express();
const logger = require('./startup/logger');
const globalErrorHandler = require('./utilities/errorHandling/globalErrorHandler');

Check warning on line 7 in src/app.js

View workflow job for this annotation

GitHub Actions / Lint Check

There should be no empty line between import groups
// const experienceRoutes = require('./routes/applicantAnalyticsRoutes');

logger.init();
Expand All @@ -15,12 +15,12 @@
require('./startup/compression')(app);
require('./startup/cors')(app);
require('./startup/bodyParser')(app);
require('./startup/session')(app); // Add session before middleware and routes
require('./startup/session')(app); // Add session before middleware and routes

app.use('/api/test', testRoutes);

const helpFeedbackRouter = require('./routes/helpFeedbackRouter');
const helpRequestRouter = require('./routes/helpRequestRouter');

Check warning on line 23 in src/app.js

View workflow job for this annotation

GitHub Actions / Lint Check

There should be no empty line between import groups

app.use('/api/feedback', helpFeedbackRouter);
app.use('/api/helprequest', helpRequestRouter);
Expand Down
2 changes: 1 addition & 1 deletion src/controllers/bmdashboard/bmEquipmentController.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,7 +298,7 @@ const bmEquipmentController = (BuildingEquipment) => {
}

if (!mongoose.Types.ObjectId.isValid(createdBy)) {
console.error('Invalid createdBy ID:', createdBy);
console.error('Invalid createdBy ID');
return res.status(400).send({
error: 'Invalid user ID format. Please log in again.',
details: `Expected a valid MongoDB ObjectId, got: ${createdBy}`,
Expand Down
162 changes: 162 additions & 0 deletions src/controllers/calendarController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
const calendarController = function (CalendarEvent, ProcessingProject) {
const getCalendarEvents = async (req, res) => {
try {
const { month, year, module: moduleFilter } = req.query;

if (!month || !year) {
return res.status(400).send({ error: 'Month and year query parameters are required.' });
}

const monthNum = Number.parseInt(month, 10);
const yearNum = Number.parseInt(year, 10);

if (Number.isNaN(monthNum) || Number.isNaN(yearNum) || monthNum < 1 || monthNum > 12) {
return res.status(400).send({ error: 'Invalid month or year value.' });
}

// Build date range: start of month to end of month
const startDate = new Date(yearNum, monthNum - 1, 1);
const endDate = new Date(yearNum, monthNum, 0, 23, 59, 59, 999);

// Build query from validated primitives only — never spread user-controlled data
const safeQuery = {
scheduled_date: { $gte: startDate, $lte: endDate },
};

const validModules = ['garden', 'orchard', 'animals', 'kitchen'];

if (moduleFilter && !validModules.includes(moduleFilter)) {
return res.status(400).send({
error: `Invalid module. Must be one of: ${validModules.join(', ')}`,
});
}

let events = [];

if (moduleFilter === 'kitchen') {
// Query kitchen events from ProcessingProject collection
const kitchenProjects = await ProcessingProject.find(safeQuery).sort({
scheduled_date: 1,
});
events = kitchenProjects.map((project) => ({
_id: project._id,
title: project.item_name,
module: 'kitchen',
event_type: project.process_name,
scheduled_date: project.scheduled_date,
description: '',
assigned_to: '',
related_item: project.item_name,
status: 'scheduled',
}));
} else if (moduleFilter) {
// Look up the module from the whitelist — never use raw user value in query
const safeModule = validModules.find((m) => m === moduleFilter);
events = await CalendarEvent.find({ ...safeQuery, module: safeModule }).sort({
scheduled_date: 1,
});
} else {
// No filter: query all modules in parallel
const [calendarEvents, kitchenProjects] = await Promise.all([
CalendarEvent.find(safeQuery).sort({ scheduled_date: 1 }),
ProcessingProject.find(safeQuery).sort({ scheduled_date: 1 }),
]);

const normalizedKitchen = kitchenProjects.map((project) => ({
_id: project._id,
title: project.item_name,
module: 'kitchen',
event_type: project.process_name,
scheduled_date: project.scheduled_date,
description: '',
assigned_to: '',
related_item: project.item_name,
status: 'scheduled',
}));

events = [...calendarEvents, ...normalizedKitchen];

// Sort merged results by scheduled_date
events.sort((a, b) => new Date(a.scheduled_date) - new Date(b.scheduled_date));
}

return res.status(200).send({ events });
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching calendar events:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

const createEvent = async (req, res) => {
try {
const {
title,
module: eventModule,
event_type,
scheduled_date,
description,
assigned_to,
related_item,
status,
} = req.body;

if (!title || !eventModule || !event_type || !scheduled_date) {
return res.status(400).send({
error: 'Title, module, event_type, and scheduled_date are required.',
});
}

const validModules = ['garden', 'orchard', 'animals', 'kitchen'];
if (!validModules.includes(eventModule)) {
return res.status(400).send({
error: `Invalid module. Must be one of: ${validModules.join(', ')}`,
});
}

const event = new CalendarEvent({
title,
module: eventModule,
event_type,
scheduled_date,
description,
assigned_to,
related_item,
status,
});

await event.save();
return res.status(201).send(event);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error creating calendar event:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

const deleteEvent = async (req, res) => {
try {
const { id } = req.params;

const event = await CalendarEvent.findById(id);
if (!event) {
return res.status(404).send({ error: 'Event not found.' });
}

await CalendarEvent.findByIdAndDelete(id);
return res.status(200).send({ message: 'Event successfully deleted.' });
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error deleting calendar event:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

return {
getCalendarEvents,
createEvent,
deleteEvent,
};
};

module.exports = calendarController;
54 changes: 54 additions & 0 deletions src/controllers/cullingController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
const cullingController = function (CullingEvent) {
const postEvent = async (req, res) => {
try {
const { name, related_to, count, purpose, notes, scheduled_date } = req.body;

if (!name || !related_to || !count || !purpose || !scheduled_date) {
return res.status(400).send({
error: 'Name, related_to, count, purpose, and scheduled_date are required.',
});
}

const validModules = ['Garden', 'Orchard', 'Animals'];
if (!validModules.includes(related_to)) {
return res.status(400).send({
error: `Invalid related_to value. Must be one of: ${validModules.join(', ')}`,
});
}

const event = new CullingEvent({
name,
related_to,
count,
purpose,
notes,
scheduled_date,
});

await event.save();
return res.status(201).send(event);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error creating culling event:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

const getEvents = async (req, res) => {
try {
const events = await CullingEvent.find().sort({ scheduled_date: 1 });
return res.status(200).send(events);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching culling events:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

return {
postEvent,
getEvents,
};
};

module.exports = cullingController;
73 changes: 73 additions & 0 deletions src/controllers/harvestingController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
const harvestingController = function (HarvestingEvent) {
const postEvent = async (req, res) => {
try {
const { name, related_to, type, expected_date, yield: yieldAmount } = req.body;

if (!name || !related_to || !type || !expected_date) {
return res.status(400).send({
error: 'Name, related_to, type, and expected_date are required.',
});
}

const validModules = ['Garden', 'Orchard', 'Animals'];
if (!validModules.includes(related_to)) {
return res.status(400).send({
error: `Invalid related_to value. Must be one of: ${validModules.join(', ')}`,
});
}

const validTypes = ['garden harvesting', 'orchard harvesting'];
if (!validTypes.includes(type)) {
return res.status(400).send({
error: `Invalid type value. Must be one of: ${validTypes.join(', ')}`,
});
}

const event = new HarvestingEvent({
name,
related_to,
type,
expected_date,
yield: yieldAmount,
});

await event.save();
return res.status(201).send(event);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error creating harvesting event:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

const getEvents = async (req, res) => {
try {
const { type } = req.query;
const VALID_TYPES = ['garden harvesting', 'orchard harvesting'];

if (type && !VALID_TYPES.includes(type)) {
return res.status(400).send({
error: `Invalid type filter. Must be one of: ${VALID_TYPES.join(', ')}`,
});
}

// Build query from whitelisted values only — never from raw user input
const matchedType = VALID_TYPES.find((t) => t === type);
const query = matchedType ? { type: matchedType } : {};

const events = await HarvestingEvent.find(query).sort({ expected_date: 1 });
return res.status(200).send(events);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching harvesting events:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

return {
postEvent,
getEvents,
};
};

module.exports = harvestingController;
8 changes: 7 additions & 1 deletion src/controllers/lbdashboard/bidsController.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,13 @@ const bidsController = function (Bids) {

const paypalCheckoutNowLink = ordDetails?.links.find((u) => u.href.includes('checkoutnow'));
const newBidsData = {
...req.body,
listingId: req.body.listingId,
termsAgreed: req.body.termsAgreed,
email: req.body.email,
startDate: req.body.startDate,
endDate: req.body.endDate,
biddingHistory: req.body.biddingHistory,
phone: req.body.phone,
userId: userExists._id,
paypalOrderId: ordDetails?.id,
paypalCheckoutNowLink: paypalCheckoutNowLink?.href,
Expand Down
53 changes: 53 additions & 0 deletions src/controllers/plantingController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
const plantingController = function (PlantingEvent) {
const postEvent = async (req, res) => {
try {
const { name, related_to, count, date, location } = req.body;

if (!name || !related_to || !count || !date || !location) {
return res.status(400).send({
error: 'Name, related_to, count, date, and location are required.',
});
}

const validModules = ['Garden', 'Orchard', 'Animals'];
if (!validModules.includes(related_to)) {
return res.status(400).send({
error: `Invalid related_to value. Must be one of: ${validModules.join(', ')}`,
});
}

const event = new PlantingEvent({
name,
related_to,
count,
date,
location,
});

await event.save();
return res.status(201).send(event);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error creating planting event:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

const getEvents = async (req, res) => {
try {
const events = await PlantingEvent.find().sort({ date: 1 });
return res.status(200).send(events);
} catch (err) {
// eslint-disable-next-line no-console
console.error('Error fetching planting events:', err);
return res.status(500).send({ error: 'Internal Server Error' });
}
};

return {
postEvent,
getEvents,
};
};

module.exports = plantingController;
Loading
Loading