git-webhooks/webhooks.js

101 lines
3.2 KiB
JavaScript

/* 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
// Use spacing of 2 for Forgejo, or none for GitHub
hmac.update(JSON.stringify(req.body, null, 2))
// 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}`)
})