#!/usr/bin/env node const os = require('os') const fs = require('fs') const path = require('path') const express = require('express') const app = express() const https = require('https') const http = require('http') const bodyParser = require('body-parser') const serveStatic = require('serve-static') const cookieParser = require('cookie-parser') const argv = require('minimist')(process.argv.slice(2)) const got = require('got') const morgan = require('morgan') const accounts = require('../lib/admin/accounts') const machines = require('../lib/admin/machines') const config = require('../lib/admin/config') const login = require('../lib/admin/login') const pairing = require('../lib/admin/pairing') const server = require('../lib/admin/server') const transactions = require('../lib/admin/transactions') const devMode = argv.dev let serverConfig try { const homeConfigPath = path.resolve(os.homedir(), '.lamassu', 'lamassu.json') serverConfig = JSON.parse(fs.readFileSync(homeConfigPath)) } catch (_) { try { const globalConfigPath = path.resolve('/etc', 'lamassu', 'lamassu.json') serverConfig = JSON.parse(fs.readFileSync(globalConfigPath)) } catch (_) { console.error("Couldn't open config file.") process.exit(1) } } const hostname = serverConfig.hostname if (!hostname) { console.error('Error: no hostname specified.') process.exit(1) } function dbNotify () { return got.post('http://localhost:3030/dbChange') .catch(e => console.error('Error: lamassu-server not responding')) } const skip = (req, res) => req.path === '/api/status/' && res.statusCode === 200 app.use(morgan('dev', {skip})) app.use(cookieParser()) app.use(register) if (!devMode) app.use(authenticate) app.use(bodyParser.json()) app.get('/api/totem', (req, res) => { const name = req.query.name if (!name) return res.status(400).send('Name is required') return pairing.totem(hostname, name) .then(totem => res.send(totem)) }) app.get('/api/accounts', (req, res) => { accounts.selectedAccounts() .then(accounts => res.json({accounts: accounts})) }) app.get('/api/account/:account', (req, res) => { accounts.getAccount(req.params.account) .then(account => res.json(account)) }) app.post('/api/account', (req, res) => { return accounts.updateAccount(req.body) .then(account => res.json(account)) .then(() => dbNotify()) }) app.get('/api/config/:config', (req, res) => config.fetchConfigGroup(req.params.config).then(c => res.json(c))) app.post('/api/config', (req, res, next) => { config.saveConfigGroup(req.body) .then(c => res.json(c)) .then(() => dbNotify()) .catch(next) }) app.get('/api/accounts/account/:account', (req, res) => { accounts.getAccount(req.params.account) .then(r => res.send(r)) }) app.get('/api/machines', (req, res) => { machines.getMachines() .then(r => res.send({machines: r})) }) app.post('/api/machines', (req, res) => { machines.setMachine(req.body) .then(() => machines.getMachines()) .then(r => res.send({machines: r})) .then(() => dbNotify()) }) app.get('/api/status', (req, res, next) => { return Promise.all([server.status(), config.validateCurrentConfig()]) .then(([serverStatus, invalidConfigGroups]) => res.send({ server: serverStatus, invalidConfigGroups })) .catch(next) }) app.get('/api/transactions', (req, res, next) => { return transactions.batch() .then(r => res.send({transactions: r})) .catch(next) }) app.use((err, req, res, next) => { console.error(err) return res.status(500).send(err.message) }) const options = { key: fs.readFileSync(serverConfig.keyPath), cert: fs.readFileSync(serverConfig.certPath) } app.use(serveStatic(path.resolve(__dirname, '..', 'public'))) function register (req, res, next) { const otp = req.query.otp if (!otp) return next() return login.register(otp) .then(r => { if (r.expired) return res.status(401).send('OTP expired, generate new registration link') if (!r.success) return res.status(401).send('Registration failed') const cookieOpts = { httpOnly: true, secure: true } const token = r.token req.token = token res.cookie('token', token, cookieOpts) next() }) } function authenticate (req, res, next) { const token = req.token || req.cookies.token return login.authenticate(token) .then(success => { if (!success) return res.status(401).send('Authentication failed') next() }) } process.on('unhandledRejection', err => { console.error(err.stack) process.exit(1) }) if (devMode) { http.createServer(app).listen(8070, () => { console.log('lamassu-admin-server listening on port 8070') }) } else { https.createServer(options, app).listen(443, () => { console.log('lamassu-admin-server listening on port 443') }) }