Incollato originale
This commit is contained in:
parent
bdc8ee6beb
commit
a472c1bf0a
1 changed files with 194 additions and 0 deletions
194
lib/webhook/index.js
Normal file
194
lib/webhook/index.js
Normal file
|
@ -0,0 +1,194 @@
|
||||||
|
/**
|
||||||
|
* Webhooks listener
|
||||||
|
*
|
||||||
|
* Create webhooks listener
|
||||||
|
*
|
||||||
|
* @file This files defines the webhooks listener
|
||||||
|
* @author nioc
|
||||||
|
* @since 1.0.0
|
||||||
|
* @license AGPL-3.0+
|
||||||
|
*/
|
||||||
|
|
||||||
|
module.exports = (logger, config, xmpp) => {
|
||||||
|
const http = require('http')
|
||||||
|
const express = require('express')
|
||||||
|
const bodyParser = require('body-parser')
|
||||||
|
const basicAuth = require('express-basic-auth')
|
||||||
|
const jmespath = require('jmespath')
|
||||||
|
const port = config.listener.port || 8000
|
||||||
|
const portSsl = config.listener.ssl.port || 8001
|
||||||
|
|
||||||
|
const webhook = express()
|
||||||
|
|
||||||
|
// handle connection from proxy (get IP in 'X-Forwarded-For' header)
|
||||||
|
webhook.set('trust proxy', true)
|
||||||
|
|
||||||
|
// logs request
|
||||||
|
if (config.listener.log.active) {
|
||||||
|
const morgan = require('morgan')
|
||||||
|
const fs = require('fs')
|
||||||
|
// create path if not exists
|
||||||
|
if (!fs.existsSync(config.listener.log.path)) {
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(config.listener.log.path)
|
||||||
|
} catch (error) {
|
||||||
|
logger.fatal(`Can not create webhooks log folder: ${error.message}`)
|
||||||
|
process.exit(99)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create log
|
||||||
|
const accessLogStream = fs.createWriteStream(config.listener.log.path + config.listener.log.filename, { flags: 'a' })
|
||||||
|
accessLogStream.on('error', (err) => {
|
||||||
|
logger.fatal('Can not create webhooks log file: ' + err.message)
|
||||||
|
process.exit(99)
|
||||||
|
})
|
||||||
|
webhook.use(morgan('combined', { stream: accessLogStream }))
|
||||||
|
}
|
||||||
|
|
||||||
|
// parse request
|
||||||
|
webhook.use(bodyParser.json())
|
||||||
|
|
||||||
|
// add basic authentification
|
||||||
|
webhook.use(basicAuth({
|
||||||
|
users: config.listener.users,
|
||||||
|
unauthorizedResponse: 'Invalid authorization'
|
||||||
|
}))
|
||||||
|
|
||||||
|
// handle post request
|
||||||
|
webhook.post(config.listener.path + '/*', (req, res) => {
|
||||||
|
logger.info(`Incoming webhook from ${req.auth.user}`)
|
||||||
|
const webhook = config.getWebhookAction(req.path)
|
||||||
|
if (!webhook) {
|
||||||
|
logger.error(`Webhook received: ${req.path}, not found`)
|
||||||
|
return res.status(404).send('Webhook not found')
|
||||||
|
}
|
||||||
|
logger.debug(`Webhook received: ${webhook.path}, start action: ${webhook.action}`)
|
||||||
|
logger.trace(req.body)
|
||||||
|
switch (webhook.action) {
|
||||||
|
case 'send_xmpp_message': {
|
||||||
|
// get destination
|
||||||
|
if ('destination' in req.body === false) {
|
||||||
|
logger.error('Destination not found')
|
||||||
|
return res.status(400).send('Destination not found')
|
||||||
|
}
|
||||||
|
const destination = req.body.destination
|
||||||
|
|
||||||
|
// get message
|
||||||
|
if ('message' in req.body === false) {
|
||||||
|
logger.error('Message not found')
|
||||||
|
return res.status(400).send('Message not found')
|
||||||
|
}
|
||||||
|
const message = req.body.message
|
||||||
|
|
||||||
|
// check if destination is a group chat
|
||||||
|
const type = config.xmpp.rooms.some((room) => room.id === destination) ? 'groupchat' : 'chat'
|
||||||
|
|
||||||
|
// send message
|
||||||
|
logger.trace(`Send to ${destination} (group:${type}) following message :\r\n${message}`)
|
||||||
|
xmpp.send(destination, message, type)
|
||||||
|
.then(() => {
|
||||||
|
return res.status(200).send({ status: 'ok', destination })
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return res.status(500).send('Could not send message')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
case 'send_xmpp_template': {
|
||||||
|
// bind data in template
|
||||||
|
const msg = webhook.template.replace(/\$\{(.+?)\}/g, (match, $1) => {
|
||||||
|
return jmespath.search(req.body, $1) || ''
|
||||||
|
})
|
||||||
|
logger.trace(`Message:\r\n${msg}`)
|
||||||
|
logger.trace(`Arguments: ${webhook.args}`)
|
||||||
|
|
||||||
|
// send message
|
||||||
|
logger.trace(`Send to ${webhook.args.destination} (group:${webhook.args.type}) following message :\r\n${msg}`)
|
||||||
|
xmpp.send(webhook.args.destination, msg, webhook.args.type)
|
||||||
|
.then(() => {
|
||||||
|
return res.status(200).send('ok')
|
||||||
|
})
|
||||||
|
.catch(() => {
|
||||||
|
return res.status(500).send('Could not send message')
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return res.status(204).send()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle non post requests
|
||||||
|
webhook.all('*', (req, res) => {
|
||||||
|
return res.status(405).send('Method not allowed')
|
||||||
|
})
|
||||||
|
|
||||||
|
// handle server error
|
||||||
|
webhook.on('error', (error) => {
|
||||||
|
logger.error('Error', error)
|
||||||
|
})
|
||||||
|
|
||||||
|
// get IP v4 addresses and prepare endpoints for output
|
||||||
|
let addresses = []
|
||||||
|
const networkInterfaces = require('os').networkInterfaces()
|
||||||
|
for (const ifaceName in networkInterfaces) {
|
||||||
|
addresses = addresses.concat(networkInterfaces[ifaceName].reduce((add, iface) => {
|
||||||
|
if (iface.family === 'IPv4') {
|
||||||
|
add.push(iface.address)
|
||||||
|
}
|
||||||
|
return add
|
||||||
|
}, []))
|
||||||
|
}
|
||||||
|
|
||||||
|
// start HTTP listener
|
||||||
|
const httpServer = http.createServer(webhook).listen(port, () => {
|
||||||
|
let endpoints = `http://localhost:${port}${config.listener.path}`
|
||||||
|
addresses.forEach(address => {
|
||||||
|
endpoints += ` http://${address}:${port}${config.listener.path}`
|
||||||
|
})
|
||||||
|
logger.info(`Listening webhooks on ${endpoints}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
// start HTTPS listener
|
||||||
|
if (config.listener.ssl.port !== null) {
|
||||||
|
if (process.getuid) {
|
||||||
|
logger.debug(`App is started with uid: ${process.getuid()}`)
|
||||||
|
}
|
||||||
|
logger.debug(`Start HTTPS on port ${portSsl}, private key: ${config.listener.ssl.keyPath}, cert: ${config.listener.ssl.certPath}`)
|
||||||
|
const https = require('https')
|
||||||
|
const fs = require('fs')
|
||||||
|
// check if cert is readable
|
||||||
|
try {
|
||||||
|
fs.accessSync(config.listener.ssl.keyPath, fs.constants.R_OK)
|
||||||
|
logger.debug('Can read private key')
|
||||||
|
try {
|
||||||
|
fs.accessSync(config.listener.ssl.certPath, fs.constants.R_OK)
|
||||||
|
logger.debug('Can read certificate')
|
||||||
|
const credentials = {
|
||||||
|
key: fs.readFileSync(config.listener.ssl.keyPath),
|
||||||
|
cert: fs.readFileSync(config.listener.ssl.certPath)
|
||||||
|
}
|
||||||
|
https.createServer(credentials, webhook).listen(portSsl, () => {
|
||||||
|
let endpoints = `https://localhost:${portSsl}${config.listener.path}`
|
||||||
|
addresses.forEach(address => {
|
||||||
|
endpoints += ` https://${address}:${portSsl}${config.listener.path}`
|
||||||
|
})
|
||||||
|
logger.info(`Listening webhooks on ${endpoints}`)
|
||||||
|
})
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Can not read certificate: ${err.message}`)
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
logger.error(`Can not read private key: ${err.message}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Closing HTTP listener (for test)
|
||||||
|
webhook.close = () => {
|
||||||
|
httpServer.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
return webhook
|
||||||
|
}
|
Loading…
Reference in a new issue