/* jshint esversion: 8 */ import { promisify } from 'util' import child_process from 'child_process' const exec = promisify(child_process.exec) // run child_process.exec as a Promise/async import { createHmac } from 'crypto' import express from 'express' const port = process.env.PORT import { SMTPClient } from 'emailjs'; // express const app = express() app.use(express.json()) function sendEmail(msg, trigger) { const client = new SMTPClient({ user: process.env.EMAIL_USER, password: process.env.EMAIL_PASSWORD, host: process.env.SMTP_DOMAIN, ssl: true, }); // send the message and get a callback with an error or details of the message that was sent client.send( { text: `Git webhook for ${trigger} has triggered a "git pull" event with the following result:\n\n${msg}`, from: `Webhook Alerts<${process.env.EMAIL_SEND_ADDRESS}>`, to: process.env.EMAIL_RECEIVE_ADDRESS, subject: `Git service triggered a pull for ${trigger}`, }, (err, message) => { console.log(err || message); } ); } // function to run git pull async function gitPull(local_repo, res) { try { const { stdout, stderr } = await exec(`cd ${local_repo} && git pull`); let msg = stderr ? stderr : stdout // message is the error message if there is one, else the stdout sendEmail(msg, process.env.APP_PATH) res.status(200).send('Ok') } catch (err) { console.error(err) res.status(500).send('server error - sorry about that') } } // we are *recieving* POST requests here app.post(`/${process.env.APP_PATH}`, (req, res) => { const hmac = createHmac('sha256', process.env.SECRET) const local_repo = process.env.LOCAL_REPO hmac.update(JSON.stringify(req.body)) // check has signature header and the decrypted signature matches if (req.get('X-Hub-Signature-256')) { if ( `sha256=${hmac.digest('hex').toString()}` === req.get('X-Hub-Signature-256') ){ if (req.body.ref === 'refs/heads/main') { gitPull(local_repo, res) } else { console.log('Ignoring push to non-default branch') res.status(200).send('Ignoring push to non-default branch') } } else { console.error('signature header received but hash did not match') res.status(403).send('Signature is missing or does not match') } } else { console.error('Signature missing') res.status(403).send('Signature is missing or does not match') } }) // everything else should 404 app.use(function (req, res) { res.status(404).send('Nothing here') }) app.listen(port, () => { const secrets = { "PORT": process.env.PORT, "SECRET": process.env.SECRET, "EMAIL_USER": process.env.EMAIL_USER, "EMAIL_PASSWORD": process.env.EMAIL_PASSWORD, "SMTP_DOMAIN": process.env.SMTP_DOMAIN, "EMAIL_SEND_ADDRESS": process.env.EMAIL_SEND_ADDRESS, "EMAIL_RECEIVE_ADDRESS": process.env.EMAIL_RECEIVE_ADDRESS, "APP_PATH": process.env.APP_PATH, "LOCAL_REPO": process.env.LOCAL_REPO } for (let key in secrets) { if (secrets[key] === undefined || null) { throw Error(`Missing ${key} from environment values`) } } console.log(`Webhooks app listening on port ${port}`) })