@windingtree/wt-write-api
Version:
API to write data to the Winding Tree platform
108 lines (92 loc) • 3.6 kB
JavaScript
const express = require('express');
const bodyParser = require('body-parser');
const morgan = require('morgan');
const swaggerUi = require('swagger-ui-express');
const cors = require('cors');
const slash = require('express-slash');
const path = require('path');
const YAML = require('yamljs');
const config = require('./config');
const WT = require('./services/wt');
const { version } = require('../package.json');
const { HttpError, HttpInternalError, Http404Error, HttpBadRequestError } = require('./errors');
const { attachAccount, handleOnChainErrors, handleDataFetchingErrors } = require('./middleware');
const { createHotel, updateHotel, forceUpdateHotel, deleteHotel, getHotel,
transferHotel } = require('./controllers/hotels');
const { createAccount, updateAccount, deleteAccount } = require('./controllers/accounts');
const app = express();
// No need to leak information and waste bandwith with this
// header.
app.disable('x-powered-by');
app.enable('strict routing');
// Swagger docs.
const swaggerDocument = YAML.load(path.resolve(__dirname, '../docs/swagger.yaml'));
swaggerDocument.servers = [{ url: config.baseUrl }];
swaggerDocument.info.version = version;
app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument));
app.use(cors());
app.use(bodyParser.json());
app.use((err, req, res, next) => {
// Catch and handle bodyParser errors.
if (err.statusCode === 400 && err.type === 'entity.parse.failed') {
return next(new HttpBadRequestError('badRequest', 'Invalid JSON.'));
}
next(err);
});
// Logg HTTP requests.
app.use(morgan(':remote-addr :remote-user [:date[clf]] :method :url HTTP/:http-version :status :res[content-length] - :response-time ms', {
stream: {
write: (msg) => config.logger.info(msg),
},
}));
// Root handler
app.get('/', async (req, res) => {
const wt = WT.get();
res.status(200).json({
docs: `${config.baseUrl}/docs/`,
info: 'https://github.com/windingtree/wt-write-api/blob/master/README.md',
version,
config: process.env.WT_CONFIG,
ethNetwork: config.ethNetwork,
entrypointAddress: config.entrypointAddress,
wtDirectoryAddress: await wt.getDirectoryAddress(),
wtFactoryAddress: await wt.getFactoryAddress(),
dataFormatVersions: config.dataFormatVersions,
});
});
// Accounts
const router = express.Router({
strict: true,
});
// NOTE: For security reasons, accounts are write only.
router.post('/accounts', createAccount);
router.put('/accounts/:id', attachAccount, updateAccount);
router.delete('/accounts/:id', attachAccount, deleteAccount);
// Hotels
router.post('/hotels', attachAccount, createHotel, handleOnChainErrors);
router.get('/hotels/:address', getHotel, handleOnChainErrors);
router.delete('/hotels/:address', attachAccount, deleteHotel, handleOnChainErrors);
router.patch('/hotels/:address', attachAccount, updateHotel, handleOnChainErrors, handleDataFetchingErrors);
router.put('/hotels/:address', attachAccount, forceUpdateHotel, handleOnChainErrors, handleDataFetchingErrors);
router.post('/hotels/:address/transfer', attachAccount, transferHotel, handleOnChainErrors);
app.use(router);
app.use(slash());
// 404 handler
app.use('*', (req, res, next) => {
next(new Http404Error());
});
// Error handler
app.use((err, req, res, next) => {
if (!(err instanceof HttpError)) {
config.logger.error(err.stack);
err = new HttpInternalError(null, err.originalError, err.message);
}
const response = res.status(err.status);
if (err.headers) {
response.set(err.headers);
}
response.json(err.toPlainObject());
});
module.exports = {
app,
};