Skip to content
Open
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
217 changes: 216 additions & 1 deletion src/createServer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,222 @@
'use strict';

const cors = require('cors');
const express = require('express');
const path = require('path');
const { Op } = require('sequelize');
const {
models: { User, Expense, Category },
} = require('./models/models');

const createServer = () => {
// your code goes here
const app = express();
const usersRouter = express.Router();
const expensesRouter = express.Router();

app.use(cors());
app.use(express.json());
Comment on lines +16 to +17
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The task requirement specifies setting the header Content-Type: application/json; charset=utf-8 on all responses. While res.json() sets Content-Type: application/json, it may not include the charset=utf-8 part. Consider adding a middleware to set this header globally.


// --- Users ---

usersRouter.post('/', async (req, res) => {
const { name } = req.body;

if (!name) {
return res.status(400).json({ message: 'Name is required' });
}

const user = await User.create({ name });

res.status(201).json(user);
});

usersRouter.get('/', async (req, res) => {
const users = await User.findAll();

res.json(users);
});

usersRouter.get('/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

res.json(user);
});

usersRouter.patch('/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

await user.update(req.body);

res.json(user);
});

usersRouter.delete('/:id', async (req, res) => {
const user = await User.findByPk(req.params.id);

if (!user) {
return res.status(404).json({ message: 'User not found' });
}

await user.destroy();

res.status(204).send();
});

// --- Expenses ---
expensesRouter.post('/', async (req, res) => {
const { userId, spentAt, title, amount } = req.body;

if (!userId || !spentAt || !title || amount === undefined) {
return res.status(400).json({ message: 'Missing required fields' });
}

const user = await User.findByPk(userId);

if (!user) {
return res.status(400).json({ message: 'User not found' });
}

const expense = await Expense.create(req.body);

res.status(201).json(expense);
});

expensesRouter.get('/', async (req, res) => {
const { userId, from, to, categories } = req.query;
const where = {};

if (userId !== undefined) {
where.userId = Number(userId);
}

if (from !== undefined || to !== undefined) {
where.spentAt = {};

if (from !== undefined) {
where.spentAt[Op.gte] = new Date(from);
}

if (to !== undefined) {
where.spentAt[Op.lte] = new Date(to);
}
}

if (categories !== undefined) {
where.category = { [Op.in]: categories.split(',') };
}

const expenses = await Expense.findAll({ where });

res.json(expenses);
});

expensesRouter.get('/:id', async (req, res) => {
const expense = await Expense.findByPk(req.params.id);

if (!expense) {
return res.status(404).json({ message: 'Expense not found' });
}

res.json(expense);
});

expensesRouter.patch('/:id', async (req, res) => {
const expense = await Expense.findByPk(req.params.id);

if (!expense) {
return res.status(404).json({ message: 'Expense not found' });
}

await expense.update(req.body);

res.json(expense);
});

expensesRouter.delete('/:id', async (req, res) => {
const expense = await Expense.findByPk(req.params.id);

if (!expense) {
return res.status(404).json({ message: 'Expense not found' });
}

await expense.destroy();

res.status(204).send();
});

// --- Categories ---

const categoriesRouter = express.Router();

categoriesRouter.post('/', async (req, res) => {
const { name } = req.body;

if (!name) {
return res.status(400).json({ message: 'Name is required' });
}

const category = await Category.create({ name });

res.status(201).json(category);
});

categoriesRouter.get('/', async (req, res) => {
const categories = await Category.findAll();

res.json(categories);
});

categoriesRouter.get('/:id', async (req, res) => {
const category = await Category.findByPk(req.params.id);

if (!category) {
return res.status(404).json({ message: 'Category not found' });
}

res.json(category);
});

categoriesRouter.patch('/:id', async (req, res) => {
const category = await Category.findByPk(req.params.id);

if (!category) {
return res.status(404).json({ message: 'Category not found' });
}

await category.update(req.body);

res.json(category);
});

categoriesRouter.delete('/:id', async (req, res) => {
const category = await Category.findByPk(req.params.id);

if (!category) {
return res.status(404).json({ message: 'Category not found' });
}

await category.destroy();

res.status(204).send();
});

app.use('/users', usersRouter);
app.use('/expenses', expensesRouter);
app.use('/categories', categoriesRouter);

app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, '..', 'page.html'));
});

return app;
};

module.exports = {
Expand Down
2 changes: 1 addition & 1 deletion src/db.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ const sequelize = new Sequelize({
host: POSTGRES_HOST || 'localhost',
dialect: 'postgres',
port: POSTGRES_PORT || 5432,
password: POSTGRES_PASSWORD || '123',
password: POSTGRES_PASSWORD || 'postgre',
});

module.exports = {
Expand Down
9 changes: 7 additions & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@
'use strict';

const { createServer } = require('./createServer');
const { sequelize } = require('./db');

createServer().listen(5700, () => {
console.log('Server is running on localhost:5700');
require('./models/models');

sequelize.sync().then(() => {
createServer().listen(5700, () => {
console.log('Server is running on localhost:5700');
});
});
21 changes: 21 additions & 0 deletions src/models/Category.model.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
'use strict';

const { DataTypes } = require('sequelize');
const { sequelize } = require('../db.js');

const Category = sequelize.define(
'Category',
{
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
timestamps: false,
},
);

module.exports = {
Category,
};
30 changes: 29 additions & 1 deletion src/models/Expense.model.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
'use strict';

const { DataTypes } = require('sequelize');
const { sequelize } = require('../db.js');

const Expense = sequelize.define(
// your code goes here
'Expense',
{
spentAt: {
type: DataTypes.DATE,
allowNull: false,
},
title: {
type: DataTypes.STRING,
allowNull: false,
},
amount: {
type: DataTypes.FLOAT,
allowNull: false,
},
category: {
type: DataTypes.STRING,
},
note: {
type: DataTypes.STRING,
},
userId: {
type: DataTypes.INTEGER,
allowNull: false,
},
},
{
timestamps: false,
},
);

module.exports = {
Expand Down
12 changes: 11 additions & 1 deletion src/models/User.model.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,19 @@
'use strict';

const { DataTypes } = require('sequelize');
const { sequelize } = require('../db.js');

const User = sequelize.define(
// your code goes here
'User',
{
name: {
type: DataTypes.STRING,
allowNull: false,
},
},
{
timestamps: false,
},
);

module.exports = {
Expand Down
5 changes: 5 additions & 0 deletions src/models/models.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

const { User } = require('./User.model');
const { Expense } = require('./Expense.model');
const { Category } = require('./Category.model');

User.hasMany(Expense, { foreignKey: 'userId', constraints: false });
Expense.belongsTo(User, { foreignKey: 'userId', constraints: false });

module.exports = {
models: {
User,
Expense,
Category,
},
};
Loading