diff --git a/src/createServer.js b/src/createServer.js index 1ea5542d6..6f4e33199 100644 --- a/src/createServer.js +++ b/src/createServer.js @@ -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()); + + // --- 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 = { diff --git a/src/db.js b/src/db.js index 1ba3046cc..6899eec41 100644 --- a/src/db.js +++ b/src/db.js @@ -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 = { diff --git a/src/index.js b/src/index.js index 541737327..e49c2e2a5 100644 --- a/src/index.js +++ b/src/index.js @@ -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'); + }); }); diff --git a/src/models/Category.model.js b/src/models/Category.model.js new file mode 100644 index 000000000..8935025ed --- /dev/null +++ b/src/models/Category.model.js @@ -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, +}; diff --git a/src/models/Expense.model.js b/src/models/Expense.model.js index 567e1c3e7..716eafe82 100644 --- a/src/models/Expense.model.js +++ b/src/models/Expense.model.js @@ -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 = { diff --git a/src/models/User.model.js b/src/models/User.model.js index 61861c9e4..cf0820042 100644 --- a/src/models/User.model.js +++ b/src/models/User.model.js @@ -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 = { diff --git a/src/models/models.js b/src/models/models.js index b43b55752..00bbb186f 100644 --- a/src/models/models.js +++ b/src/models/models.js @@ -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, }, };