Files
testRestApi/node/routes/api.js
T

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;