Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
193 changes: 193 additions & 0 deletions server/controllers/expense.controller.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
import { validationResult } from 'express-validator';
import {
createExpense,
getExpenseById,
getAllExpenses,
updateExpense,
deleteExpense
} from '../services/expense.service.js';

// @desc Create a new expense
// @route POST /api/expenses
// @access Private
export const createExpenseController = async (req, res) => {
// Validate request body
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}

try {
const userId = req.user.user_id;
const expenseData = {
...req.body,
user_id: userId
};

const result = await createExpense(userId,expenseData);

if (!result.success) {
return res.status(400).json({
success: false,
error: result.error
});
}

return res.status(201).json({
success: true,
data: result.data
});
} catch (error) {
console.error('Error creating expense:', error);
return res.status(500).json({
success: false,
error: 'Server error while creating expense'
});
}
};

// @desc Get a single expense by ID
// @route GET /api/expenses/:id
// @access Private
export const getExpenseController = async (req, res) => {
try {
const { id } = req.params;
const userId = req.user.user_id;

const result = await getExpenseById(id, userId);

if (!result.success) {
return res.status(404).json({
success: false,
error: result.error
});
}

return res.status(200).json({
success: true,
data: result.data
});
} catch (error) {
console.error('Error fetching expense:', error);
return res.status(500).json({
success: false,
error: 'Server error while fetching expense'
});
}
};

// @desc Get all expenses for a user
// @route GET /api/expenses
// @access Private
export const getAllExpensesController = async (req, res) => {
try {
const userId = req.user.user_id;
const { startDate, endDate, categoryId, type } = req.query;

const result = await getAllExpenses(userId, {
startDate,
endDate,
categoryId,
type
});

if (!result.success) {
return res.status(400).json({
success: false,
error: result.error
});
}

return res.status(200).json({
success: true,
data: result.data,
count: result.data.length
});
} catch (error) {
console.error('Error fetching expenses:', error);
return res.status(500).json({
success: false,
error: 'Server error while fetching expenses'
});
}
};

// @desc Update an expense
// @route PUT /api/expenses/:id
// @access Private
export const updateExpenseController = async (req, res) => {
// Validate request body
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({
success: false,
errors: errors.array()
});
}

try {
const { id } = req.params;
const userId = req.user.user_id;
const updateData = req.body;

// Don't allow updating user_id
if (updateData.user_id) {
delete updateData.user_id;
}

const result = await updateExpense(id, userId, updateData);

if (!result.success) {
const statusCode = result.error.includes('not found') ? 404 : 400;
return res.status(statusCode).json({
success: false,
error: result.error
});
}

return res.status(200).json({
success: true,
data: result.data
});
} catch (error) {
console.error('Error updating expense:', error);
return res.status(500).json({
success: false,
error: 'Server error while updating expense'
});
}
};

// @desc Delete an expense
// @route DELETE /api/expenses/:id
// @access Private
export const deleteExpenseController = async (req, res) => {
try {
const { id } = req.params;
const userId = req.user.user_id;

const result = await deleteExpense(id, userId);

if (!result.success) {
const statusCode = result.error.includes('not found') ? 404 : 400;
return res.status(statusCode).json({
success: false,
error: result.error
});
}

return res.status(200).json({
success: true,
data: { id }
});
} catch (error) {
console.error('Error deleting expense:', error);
return res.status(500).json({
success: false,
error: 'Server error while deleting expense'
});
}
};
29 changes: 29 additions & 0 deletions server/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 4 additions & 3 deletions server/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,16 @@
"license": "ISC",
"dependencies": {
"@prisma/client": "^6.14.0",
"bcrypt": "^5.1.1",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.3.1",
"express": "^4.18.2",
"express-validator": "^7.2.1",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"morgan": "^1.10.0",
"pg": "^8.11.3",
"bcrypt": "^5.1.1",
"jsonwebtoken": "^9.0.2",
"cookie-parser": "^1.4.6",
"validator": "^13.11.0"
},
"devDependencies": {
Expand Down
68 changes: 68 additions & 0 deletions server/routes/expense.route.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import express from 'express';
import { requireAuth } from '../middleware/auth.middleware.js';
import {
createExpenseController,
getExpenseController,
getAllExpensesController,
updateExpenseController,
deleteExpenseController
} from '../controllers/expense.controller.js';
import {
createExpenseValidator,
updateExpenseValidator,
getExpenseValidator,
deleteExpenseValidator,
listExpensesValidator
} from '../validators/expense.validator.js';

const router = express.Router();

// Apply authentication middleware to all routes
router.use(requireAuth);

// @route POST /api/expenses
// @desc Create a new expense
// @access Private
router.post(
'/',
createExpenseValidator,
createExpenseController
);

// @route GET /api/expenses/:id
// @desc Get a single expense by ID
// @access Private
router.get(
'/:id',
getExpenseValidator,
getExpenseController
);

// @route GET /api/expenses
// @desc Get all expenses for the authenticated user
// @access Private
router.get(
'/',
listExpensesValidator,
getAllExpensesController
);

// @route PUT /api/expenses/:id
// @desc Update an expense
// @access Private
router.put(
'/:id',
updateExpenseValidator,
updateExpenseController
);

// @route DELETE /api/expenses/:id
// @desc Delete an expense
// @access Private
router.delete(
'/:id',
deleteExpenseValidator,
deleteExpenseController
);

export default router;
3 changes: 2 additions & 1 deletion server/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { PrismaClient } from '@prisma/client';
import incomeRoutes from './routes/income.route.js';
import authRoutes from './routes/auth.route.js';
import categoryRoutes from './routes/category.route.js';

import expenseRoutes from './routes/expense.route.js';
dotenv.config();

const app = express();
Expand All @@ -24,6 +24,7 @@ app.use(cookieParser());
app.use('/api/auth', authRoutes);
app.use('/api/incomes', requireAuth, incomeRoutes);
app.use('/api/categories', categoryRoutes);
app.use('/api/expenses', expenseRoutes)

// Initialize a single Prisma client instance
const prisma = new PrismaClient();
Expand Down
Loading