203 lines
9.7 KiB
JavaScript
203 lines
9.7 KiB
JavaScript
const express = require('express');
|
|
const { Op } = require('sequelize');
|
|
const { User, Customer, Car } = require('../models');
|
|
const { authenticate, requireAdmin } = require('../middleware/auth');
|
|
const api = require('../lib/api');
|
|
|
|
const router = express.Router();
|
|
const route = api.asyncRoute;
|
|
router.use(route(authenticate));
|
|
|
|
router.post('/auth/login', route(async (req, res) => {
|
|
await req.user.update({ last_login: new Date() });
|
|
return api.response(res, api.cleanUser(req.user));
|
|
}));
|
|
|
|
router.route('/auth/me')
|
|
.get((req, res) => api.response(res, api.cleanUser(req.user)))
|
|
.put(route((req, res) => updateProfile(req, res)))
|
|
.patch(route((req, res) => updateProfile(req, res)));
|
|
|
|
async function updateProfile(req, res) {
|
|
const data = api.pick(req.body, api.PROFILE_FIELDS);
|
|
const errors = api.validateUser(data, false);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
await req.user.update(data);
|
|
return api.response(res, api.cleanUser(req.user));
|
|
}
|
|
|
|
router.route('/users')
|
|
.all(requireAdmin)
|
|
.get(route(async (req, res) => {
|
|
const options = api.pageOptions(req.query, ['id', ...api.USER_FIELDS.filter((field) => field !== 'password'), 'last_login', 'created', 'modified']);
|
|
const where = {};
|
|
if (req.query.q) where[Op.or] = ['login', 'email', 'first_name', 'last_name'].map((field) => ({ [field]: { [Op.like]: `%${req.query.q}%` } }));
|
|
if (req.query.role) where.role = req.query.role;
|
|
if (req.query.active !== undefined) where.active = req.query.active === 'true' || req.query.active === '1';
|
|
const result = await User.findAndCountAll({ where, limit: options.limit, offset: options.offset, order: [[options.sort, options.direction]] });
|
|
const total = result.count;
|
|
res.set('X-Total-Count', String(total));
|
|
return api.response(res, result.rows.map(api.cleanUser), 200, {
|
|
meta: { page: options.page, limit: options.limit, total, pages: Math.ceil(total / options.limit), has_next_page: options.page * options.limit < total, has_prev_page: options.page > 1, sort: options.sort, direction: options.direction.toLowerCase() },
|
|
});
|
|
}))
|
|
.post(route(async (req, res) => {
|
|
const data = api.pick(req.body, api.USER_FIELDS);
|
|
if (data.active === undefined) data.active = true;
|
|
if (data.role === undefined) data.role = 'user';
|
|
const errors = api.validateUser(data, true);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
const user = await User.create(data);
|
|
res.location(`/api/users/${user.id}`);
|
|
return api.response(res, api.cleanUser(user), 201);
|
|
} catch (error) { return api.fail(res, error); }
|
|
}));
|
|
|
|
router.route('/users/:id')
|
|
.all(requireAdmin)
|
|
.get(route(async (req, res) => {
|
|
const user = await User.findByPk(req.params.id);
|
|
return user ? api.response(res, api.cleanUser(user)) : api.notFound(res, 'User');
|
|
}))
|
|
.put(route((req, res) => updateUser(req, res)))
|
|
.patch(route((req, res) => updateUser(req, res)))
|
|
.delete(route(async (req, res) => {
|
|
const user = await User.findByPk(req.params.id);
|
|
if (!user) return api.notFound(res, 'User');
|
|
if (user.id === req.user.id) return res.status(400).json({ success: false, error: { code: 400, message: 'You cannot delete your own account.' } });
|
|
await user.destroy();
|
|
return api.response(res, null, 204);
|
|
}));
|
|
|
|
async function updateUser(req, res) {
|
|
const user = await User.findByPk(req.params.id);
|
|
if (!user) return api.notFound(res, 'User');
|
|
const data = api.pick(req.body, api.USER_FIELDS);
|
|
if (user.id === req.user.id) {
|
|
if (data.role !== undefined && data.role !== user.role) return res.status(400).json({ success: false, error: { code: 400, message: 'You cannot change your own role.' } });
|
|
if (data.active === false) return res.status(400).json({ success: false, error: { code: 400, message: 'You cannot deactivate your own account.' } });
|
|
}
|
|
const errors = api.validateUser(data, false);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
await user.update(data);
|
|
return api.response(res, api.cleanUser(user));
|
|
} catch (error) { return api.fail(res, error); }
|
|
}
|
|
|
|
router.route('/customers')
|
|
.get(route(async (req, res) => {
|
|
const options = api.pageOptions(req.query, ['id', 'type', 'first_name', 'last_name', 'email', 'company_name', 'city', 'country', 'created', 'modified']);
|
|
const where = {};
|
|
if (req.query.q) where[Op.or] = ['first_name', 'last_name', 'email', 'phone', 'company_name', 'city', 'tax_id'].map((field) => ({ [field]: { [Op.like]: `%${req.query.q}%` } }));
|
|
for (const field of ['type', 'city', 'country']) if (req.query[field]) where[field] = req.query[field];
|
|
const include = String(req.query.with_cars).toLowerCase() === 'true' || req.query.with_cars === '1' ? [{ model: Car, as: 'cars' }] : undefined;
|
|
return api.list(res, Customer, { ...options, where }, include);
|
|
}))
|
|
.post(route(async (req, res) => {
|
|
const data = api.pick(req.body, api.CUSTOMER_FIELDS);
|
|
if (data.type === undefined) data.type = 'individual';
|
|
if (data.country === undefined) data.country = 'PL';
|
|
const errors = api.validateCustomer(data, true);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
const customer = await Customer.create({ ...data, created_by: req.user.id });
|
|
res.location(`/api/customers/${customer.id}`);
|
|
return api.response(res, customer, 201);
|
|
} catch (error) { return api.fail(res, error); }
|
|
}));
|
|
|
|
router.route('/customers/:id')
|
|
.get(route(async (req, res) => {
|
|
const customer = await Customer.findByPk(req.params.id, { include: [{ model: Car, as: 'cars' }] });
|
|
return customer ? api.response(res, customer) : api.notFound(res, 'Customer');
|
|
}))
|
|
.put(route((req, res) => updateCustomer(req, res)))
|
|
.patch(route((req, res) => updateCustomer(req, res)))
|
|
.delete(route(async (req, res) => {
|
|
const customer = await Customer.findByPk(req.params.id);
|
|
if (!customer) return api.notFound(res, 'Customer');
|
|
await customer.destroy();
|
|
return api.response(res, null, 204);
|
|
}));
|
|
|
|
async function updateCustomer(req, res) {
|
|
const customer = await Customer.findByPk(req.params.id);
|
|
if (!customer) return api.notFound(res, 'Customer');
|
|
const data = api.pick(req.body, api.CUSTOMER_FIELDS);
|
|
const errors = api.validateCustomer(data, false);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
await customer.update(data);
|
|
return api.response(res, customer);
|
|
} catch (error) { return api.fail(res, error); }
|
|
}
|
|
|
|
router.route('/customers/:customer_id/cars')
|
|
.get(route(async (req, res) => {
|
|
const customer = await Customer.findByPk(req.params.customer_id);
|
|
if (!customer) return api.notFound(res, 'Customer');
|
|
const options = api.pageOptions(req.query, ['id', 'customer_id', 'make', 'model', 'production_year', 'registration_number', 'fuel_type', 'mileage', 'power_hp', 'created', 'modified']);
|
|
return api.list(res, Car, { ...options, where: { customer_id: customer.id } });
|
|
}))
|
|
.post(route(async (req, res) => {
|
|
const customer = await Customer.findByPk(req.params.customer_id);
|
|
if (!customer) return api.notFound(res, 'Customer');
|
|
return createCar(req, res, customer.id);
|
|
}));
|
|
|
|
router.route('/cars')
|
|
.get(route(async (req, res) => {
|
|
const options = api.pageOptions(req.query, ['id', 'customer_id', 'make', 'model', 'production_year', 'registration_number', 'fuel_type', 'mileage', 'power_hp', 'created', 'modified']);
|
|
const where = {};
|
|
if (req.query.customer_id) where.customer_id = req.query.customer_id;
|
|
if (req.query.q) where[Op.or] = ['make', 'model', 'vin', 'registration_number'].map((field) => ({ [field]: { [Op.like]: `%${req.query.q}%` } }));
|
|
for (const field of ['make', 'fuel_type', 'transmission']) if (req.query[field]) where[field] = req.query[field];
|
|
if (req.query.year_from) where.production_year = { ...(where.production_year || {}), [Op.gte]: Number(req.query.year_from) };
|
|
if (req.query.year_to) where.production_year = { ...(where.production_year || {}), [Op.lte]: Number(req.query.year_to) };
|
|
const include = String(req.query.with_customer).toLowerCase() === 'true' || req.query.with_customer === '1' ? [{ model: Customer, as: 'customer' }] : undefined;
|
|
return api.list(res, Car, { ...options, where }, include);
|
|
}))
|
|
.post(route((req, res) => createCar(req, res)));
|
|
|
|
async function createCar(req, res, customerId) {
|
|
const data = api.pick(req.body, api.CAR_FIELDS);
|
|
if (customerId !== undefined) data.customer_id = customerId;
|
|
const errors = api.validateCar(data, true);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
const car = await Car.create(data);
|
|
res.location(`/api/cars/${car.id}`);
|
|
return api.response(res, car, 201);
|
|
} catch (error) { return api.fail(res, error); }
|
|
}
|
|
|
|
router.route('/cars/:id')
|
|
.get(route(async (req, res) => {
|
|
const car = await Car.findByPk(req.params.id, { include: [{ model: Customer, as: 'customer' }] });
|
|
return car ? api.response(res, car) : api.notFound(res, 'Car');
|
|
}))
|
|
.put(route((req, res) => updateCar(req, res)))
|
|
.patch(route((req, res) => updateCar(req, res)))
|
|
.delete(route(async (req, res) => {
|
|
const car = await Car.findByPk(req.params.id);
|
|
if (!car) return api.notFound(res, 'Car');
|
|
await car.destroy();
|
|
return api.response(res, null, 204);
|
|
}));
|
|
|
|
async function updateCar(req, res) {
|
|
const car = await Car.findByPk(req.params.id);
|
|
if (!car) return api.notFound(res, 'Car');
|
|
const data = api.pick(req.body, api.CAR_FIELDS);
|
|
const errors = api.validateCar(data, false);
|
|
if (Object.keys(errors).length) return api.validationResponse(res, errors);
|
|
try {
|
|
await car.update(data);
|
|
return api.response(res, car);
|
|
} catch (error) { return api.fail(res, error); }
|
|
}
|
|
|
|
module.exports = router;
|